#ifdef COMPILE_OPENGL

#include "CBL_Debug.h"
#include "COGL_GLSLShaderAtomic.h"
#include "COGL_ParameterAtomic.h"

#include <OpenGL/OpenGL.h>
#include <OpenGL/glext.h>
//#include <cgGL.h>

CB_SUBNAMESPACE_START(OpenGL)

// ---------------------------------------------------------------------------------------------------------------------
//  CGLSLShaderAtomic
// ---------------------------------------------------------------------------------------------------------------------

CGLSLShaderAtomic::CGLSLShaderAtomic(uint32 _VertexShaderResource, uint32 _FragmentShaderResource)
:  m_ProgramResource(0)
, m_VertexShaderResource(_VertexShaderResource)
, m_FragmentShaderResource(_FragmentShaderResource)
{
	Reload();
}

// ---------------------------------------------------------------------------------------------------------------------

CGLSLShaderAtomic::~CGLSLShaderAtomic()
{
	glDetachShader(m_ProgramResource, m_VertexShaderResource); 
	glDetachShader(m_ProgramResource, m_FragmentShaderResource);
	
	glDeleteProgram(m_ProgramResource);
	glDeleteShader(m_VertexShaderResource);
	glDeleteShader(m_FragmentShaderResource);
}

// ---------------------------------------------------------------------------------------------------------------------

void CGLSLShaderAtomic::Reload()
{
	Reload(m_VertexShaderResource, m_FragmentShaderResource);
	RenderLib::IShaderAtomic::Reload();
}

// ---------------------------------------------------------------------------------------------------------------------

void CGLSLShaderAtomic::Reload(uint32 _VertexShaderResource, uint32 _FragmentShaderResource)
{
	if (m_ProgramResource != 0)
	{
		glDetachShader(m_ProgramResource, m_VertexShaderResource); 
		glDetachShader(m_ProgramResource, m_FragmentShaderResource);
		
		glDeleteProgram(m_ProgramResource);
		glDeleteShader(m_VertexShaderResource);
		glDeleteShader(m_FragmentShaderResource);
	}
	
	int Result = 0;
	
	m_ProgramResource = glCreateProgram(); 
	glAttachShader(m_ProgramResource, m_VertexShaderResource); 
	glAttachShader(m_ProgramResource, m_FragmentShaderResource); 
	
	glLinkProgram(m_ProgramResource); 
	glGetProgramiv(m_ProgramResource, GL_LINK_STATUS, &Result); 
	
	if (Result == 0); 
	{ 
		char ErrorMsg[500]; 
		glGetProgramInfoLog(m_ProgramResource, 500, NULL, ErrorMsg); 
		
		Debug::Error("GLSL shader compilation error.\n"
					 "Log says:\n%s", 
					 ErrorMsg);
	}
	
	// Reload parameters
	
	uint32 NumParameters = m_Parameters.Size();
	for (uint32 ParameterIdx=0; ParameterIdx < NumParameters; ++ParameterIdx)
	{
		const std::string& rName = m_ParameterMap.GetName(ParameterIdx);
		int32 UniformResource    = glGetUniformLocation(m_ProgramResource, rName.c_str());
		
		Debug::Assert(CB_DEBUG_MSG(UniformResource >= 0, 
					  "Parameter %s was not found during reload and will not be usable."), 
					  rName.c_str());
		
		IOpenGLParameter* pParameter = dynamic_cast<IOpenGLParameter*>(m_Parameters[ParameterIdx].GetObject());
		
		if (pParameter)
			pParameter->SetResource(UniformResource);
	}
}

// ---------------------------------------------------------------------------------------------------------------------

inline void QueryUniform(uint32 _ProgramResource, uint32 _UniformResource, GLint& _rVarSize, GLenum _VarType)
{
	static const GLsizei NameBufferSize = 255;
	static GLchar VarName[NameBufferSize];
	GLsizei NameLength;
	
	glGetActiveUniform(_ProgramResource, _UniformResource, NameBufferSize, &NameLength, &_rVarSize, &_VarType, VarName);
}

// ---------------------------------------------------------------------------------------------------------------------

RenderLib::CFloatParameterAtomicPtr CGLSLShaderAtomic::GetFloatParameter(const char* _pName)
{
	RenderLib::CParameterAtomicPtr Parameter = GetParameter(_pName);
	
	if (!Parameter.IsValid())
	{
		int32 UniformResource = glGetUniformLocation(m_ProgramResource, _pName);
		
		if (Debug::Verify((UniformResource > 0), "Shader constant %s is not used or declared in this pass."))
		{
			GLint	VarSize;
			GLenum	VarType;
			
			QueryUniform(m_ProgramResource, UniformResource, VarSize, VarType);
			
			if (Debug::Verify((VarType == GL_FLOAT) || (VarType == GL_FLOAT_VEC2) || (VarType == GL_FLOAT_VEC3)  || 
							  (VarType == GL_FLOAT_VEC4) || (VarType == GL_FLOAT_MAT4x3) || (VarType == GL_FLOAT_MAT4x2) ||
							  (VarType == GL_FLOAT_MAT3x4) || (VarType == GL_FLOAT_MAT3x2) ||
							  (VarType == GL_FLOAT_MAT2x3) || (VarType == GL_FLOAT_MAT2x4),
							  "Shader constant %s is not of type float, but requested as such.",
							  _pName))
			{
				CFloatParameterAtomic* pTypedParameter = new CFloatParameterAtomic(UniformResource, VarSize);
				return RenderLib::CFloatParameterAtomicPtr(pTypedParameter);
			}
		}
		
		return RenderLib::CFloatParameterAtomicPtr(NULL);
	}
	
	return RenderLib::CFloatParameterAtomicPtr(Parameter);
}

// ---------------------------------------------------------------------------------------------------------------------

RenderLib::CIntParameterAtomicPtr CGLSLShaderAtomic::GetIntParameter(const char* _pName)
{
	RenderLib::CParameterAtomicPtr Parameter = GetParameter(_pName);
	
	if (!Parameter.IsValid())
	{
		int32 UniformResource = glGetUniformLocation(m_ProgramResource, _pName);
		
		if (Debug::Verify((UniformResource > 0), "Shader constant %s is not used or declared in this pass."))
		{
			GLint	VarSize;
			GLenum	VarType;
			
			QueryUniform(m_ProgramResource, UniformResource, VarSize, VarType);
			
			if (Debug::Verify((VarType == GL_INT) || (VarType == GL_INT_VEC2) || (VarType == GL_INT_VEC3)  || (VarType == GL_INT_VEC4),
							  "Shader constant %s is not of type int, but requested as such.",
							  _pName))
			{
				CIntParameterAtomic* pTypedParameter = new CIntParameterAtomic(UniformResource, VarSize);
				return RenderLib::CIntParameterAtomicPtr(pTypedParameter);
			}
		}
		
		return RenderLib::CIntParameterAtomicPtr(NULL);
	}
	
	return RenderLib::CIntParameterAtomicPtr(Parameter);
}

// ---------------------------------------------------------------------------------------------------------------------

RenderLib::CBoolParameterAtomicPtr CGLSLShaderAtomic::GetBoolParameter(const char* _pName)
{
	RenderLib::CParameterAtomicPtr Parameter = GetParameter(_pName);
	
	if (!Parameter.IsValid())
	{
		int32 UniformResource = glGetUniformLocation(m_ProgramResource, _pName);
		
		if (Debug::Verify((UniformResource > 0), "Shader constant %s is not used or declared in this pass."))
		{
			GLint	VarSize;
			GLenum	VarType;
			
			QueryUniform(m_ProgramResource, UniformResource, VarSize, VarType);
			
			if (Debug::Verify((VarType == GL_BOOL) || (VarType == GL_BOOL_VEC2) || (VarType == GL_BOOL_VEC3)  || (VarType == GL_BOOL_VEC4),
							  "Shader constant %s is not of type bool, but requested as such.",
							  _pName))
			{
				CBoolParameterAtomic* pTypedParameter = new CBoolParameterAtomic(UniformResource, VarSize);
				return RenderLib::CBoolParameterAtomicPtr(pTypedParameter);
			}
		}
		
		return RenderLib::CBoolParameterAtomicPtr(NULL);
	}
	
	return RenderLib::CBoolParameterAtomicPtr(Parameter);
}

// ---------------------------------------------------------------------------------------------------------------------

RenderLib::CMatrix4x4ParameterAtomicPtr CGLSLShaderAtomic::GetMatrix4x4Parameter(const char* _pName)
{
	RenderLib::CParameterAtomicPtr Parameter = GetParameter(_pName);
	
	if (!Parameter.IsValid())
	{
		int32 UniformResource = glGetUniformLocation(m_ProgramResource, _pName);
		
		if (Debug::Verify((UniformResource > 0), "Shader constant %s is not used or declared in this pass."))
		{
			GLint	VarSize;
			GLenum	VarType;
			
			QueryUniform(m_ProgramResource, UniformResource, VarSize, VarType);
			
			if (Debug::Verify((VarType == GL_FLOAT_MAT4),
							  "Shader constant %s is not of type float4x4, but requested as such.",
							  _pName))
			{
				CMatrix4x4ParameterAtomic* pTypedParameter = new CMatrix4x4ParameterAtomic(UniformResource, VarSize);
				return RenderLib::CMatrix4x4ParameterAtomicPtr(pTypedParameter);
			}
		}
		
		return RenderLib::CMatrix4x4ParameterAtomicPtr(NULL);
	}
	
	return RenderLib::CMatrix4x4ParameterAtomicPtr(Parameter);
}

// ---------------------------------------------------------------------------------------------------------------------

void CGLSLShaderAtomic::Bind()
{
	glUseProgram(m_ProgramResource);
}

CB_SUBNAMESPACE_END
#endif